package com.pajk.user.service.credit;

import java.util.*;

import javax.annotation.Resource;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.dao.DuplicateKeyException;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.support.TransactionCallbackWithoutResult;
import org.springframework.transaction.support.TransactionTemplate;
import org.springframework.util.StringUtils;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.serializer.SerializerFeature;
import com.alibaba.rocketmq.client.producer.LocalTransactionExecuter;
import com.alibaba.rocketmq.client.producer.LocalTransactionState;
import com.alibaba.rocketmq.client.producer.SendResult;
import com.alibaba.rocketmq.common.message.Message;
import com.pajk.user.api.CreditService;
import com.pajk.user.api.UserService;
import com.pajk.user.dal.mapper.CreditHisMapper;
import com.pajk.user.dal.model.CreditHisDO;
import com.pajk.user.exception.CreditServiceException;
import com.pajk.user.model.CreditHis;
import com.pajk.user.model.User;
import com.pajk.user.service.common.metaq.MsgSenderService;
import com.pajk.user.service.common.metaq.TopicEnum;
import com.pajk.user.service.common.metaq.TransMsgSender;

/**
 * UserService: haihua.chenhh Date: 14-3-12 Time: 下午1:04
 */
public class CreditServiceImpl implements CreditService {
    private static final Logger log = LoggerFactory.getLogger(CreditServiceImpl.class);
    @Resource
    private CreditHisMapper     creditHisMapper;
    @Resource
    private UserService          userService;
    @Resource
    private TransactionTemplate transactionTemplate;

    @Resource
    private TransMsgSender      transMsgSender;
    @Resource
    private CreditServiceWlt    creditServiceWlt;
    @Resource
    private CreditRuleServiceImpl   creditRuleService;
    @Resource
    private MsgSenderService    msgSender;
    
    @Override
    public Integer addCredit(Long userId, String actionId, String actionCode, String appFrom,
                             Date actionTime) {

        final CreditRuleWrap creditRule = creditRuleService.getCredits(userId, actionCode, actionTime);
        if (creditRule.isOverTimes()) {
            return 0;
        }
        User user = userService.getUserById(userId);
        if (user == null) {
            log.error("can't find user by userId=" + userId);
            throw new CreditServiceException("can't find user by userId=" + userId);
        }
        final CreditHisDO creditHisDO = new CreditHisDO();
        creditHisDO.setUserId(userId);
        creditHisDO.setActionCode(actionCode);
        creditHisDO.setAppFrom(appFrom);
        creditHisDO.setCredit(creditRule.getCredit());
        creditHisDO.setActionTime(actionTime);
        creditHisDO.setActionId(actionId);
        String wanliId = user.getWanliId();
        //有万里通ID需要同步万里通积分
        if (!StringUtils.isEmpty(wanliId)) {
            HashMap<String, Object> map = new HashMap<String, Object>();
            map.put(CreditAddConsumer.MSG_ACTIONID, actionId);
            map.put(CreditAddConsumer.MSG_WANLIID, wanliId);
            map.put(CreditAddConsumer.MSG_USERID, userId);
            creditHisDO.setSync(CreditStatusEnum.hasWltNoSync.value());
            String jsonMsg=JSON.toJSONString(map, SerializerFeature.WriteClassName);
            transMsgSender.sendMessageInTransaction(TopicEnum.USER_ADD_CREDIT, jsonMsg,
                new LocalTransactionExecuter() {
                    @Override
                    public LocalTransactionState executeLocalTransactionBranch(final Message msg,
                                                                               final Object arg) {
                        try {
                            persistCreditToDB(creditHisDO, creditRule);
                            return LocalTransactionState.COMMIT_MESSAGE;
                        } catch (Exception e) {
                            log.error(e.getMessage(), e);
                            return LocalTransactionState.ROLLBACK_MESSAGE;
                        }
                    }
                });
        } else {
            creditHisDO.setSync(CreditStatusEnum.noWlt.value());
            persistCreditToDB(creditHisDO, creditRule);
        }

        log.info("add credit: " + " userId: " + creditHisDO.getUserId() + " actionCode: "
                 + actionCode + " Credits: " + creditHisDO.getCredit());
        return creditRule.getCredit();
    }
    
    private void persistCreditToDB(final CreditHisDO creditHisDO, final CreditRuleWrap creditRule) {
        transactionTemplate.execute(new TransactionCallbackWithoutResult() {
            public void doInTransactionWithoutResult(TransactionStatus status) {
                try {
                    creditHisMapper.insertCredit(creditHisDO);
                    userService.updateUserCredits(creditHisDO.getUserId(), creditHisDO.getCredit());
                } catch (DuplicateKeyException e1) {
                    log.error("Credit has already been inserted", e1);
                    status.setRollbackOnly();
                    throw new CreditServiceException("Credit has already been inserted", e1);
                } catch (Exception e) {
                    log.error("persistence credit to db failed", e);
                    status.setRollbackOnly();
                    creditRuleService.rollbackTimesFromTair(creditRule);
                    throw new CreditServiceException("persistence credit to db failed", e);
                }
            }
        });
    }

    @Override
    public void reduceCredit(Long userId, String actionId, String actionCode, Integer credit,
                             String appFrom) {
        User user = userService.getUserById(userId);
        if(user == null) {
            throw new CreditServiceException("user do not exist, userId is: " + userId );
        }
        String memberId = user.getWanliId();
        if(memberId == null) {
           throw new CreditServiceException("wlt Id is null, cannot consume credit");
        }
        int leftCredit = 0;
        try{
            leftCredit = creditServiceWlt.consumeCredit(memberId, credit, actionId, actionCode);
        } catch(CreditServiceException ce){ 
            if(isDuplicateActionId(ce.getMessage(), actionId, userId)){
                return;
            } else{
                throw ce;
            }
        }
        catch (Exception e) {
            throw new CreditServiceException("consume credit failed userId: " + userId
                                             + "actionCode: " + actionCode + "credit: " + credit, e);
        }

        HashMap<String, Object> msg = new HashMap<String, Object>();
        msg.put(ReduceCreditConsumer.ACTION_ID, actionId);
        msg.put(ReduceCreditConsumer.ACTION_CODE, actionCode);
        msg.put(ReduceCreditConsumer.CONSUME_CREDIT, credit);
        msg.put(ReduceCreditConsumer.LEFT_CREDIT, leftCredit);
        msg.put(ReduceCreditConsumer.USER_ID, userId);
        msg.put(ReduceCreditConsumer.ACTION_TIME, Calendar.getInstance().getTime());
        msg.put(ReduceCreditConsumer.APPFROM, appFrom);
        String jsonMsg=JSON.toJSONString(msg, SerializerFeature.WriteClassName);
        SendResult sendResult = msgSender.sendMessage(jsonMsg, TopicEnum.USER_CONSUME_CREDIT.getTopic(), TopicEnum.USER_CONSUME_CREDIT.getTags());
        log.info("reduceCredit: " + sendResult.toString());
    }
    
    private boolean isDuplicateActionId(String errMsg, String actionId, long userId) {
        try {
            int errCode = Integer.valueOf(errMsg.split("\\|")[0]);
            if (errCode == 25) {
                HashMap<String, Object> selectMap = new HashMap<String, Object>();
                selectMap.put("userId", userId);
                selectMap.put("actionId", actionId);
                CreditHisDO creditHisDO = creditHisMapper.getCreditByActionId(selectMap);
                if (creditHisDO != null) {
                    return true;
                }
            }
        } catch (Exception ignore) {
            return false;
        }
        return false;
    }   
    
    @Override
    public Integer queryWltCredit(String memberId) {
        return creditServiceWlt.queryWltCredit(memberId);
    }

    @Override
    public Integer queryWltCreditByUserId(Long userId) {
        User user = userService.getUserById(userId);
        if(user == null) {
            throw new CreditServiceException("user do not exist, userId is: " + userId );
        }
        String memberId = user.getWanliId();
        if(memberId == null) {
            throw new CreditServiceException("wlt Id is null, cannot queryWlt credit");
        }
        return creditServiceWlt.queryWltCredit(memberId);
    }

    @Override
    public boolean queryConsumeCredit(String actionId){
        return creditServiceWlt.queryConsumeCredit(actionId);
    }

    @Override
    public int cancelReduceCredit(Long userId, int credit, int cancelCredit,
                                  String cancelTTNumber, String actionId) {
        User user = userService.getUserById(userId);
        if(user == null) {
            throw new CreditServiceException("user do not exist, userId is: " + userId );
        }
        String memberId = user.getWanliId();
        if(memberId == null) {
            throw new CreditServiceException("wlt Id is null, cannot consume credit");
        }
        return creditServiceWlt.cancelConsumeCredit(memberId, credit, cancelCredit, cancelTTNumber, actionId);
    }

    @Override
    public List<CreditHis> getCreditHisByInfo(long userId, String actionCode, Date actionTime) {
        List<CreditHis> creditHisList=new ArrayList<CreditHis>();
        List<CreditHisDO> creditHisDoList=null;
        HashMap<String, Object> map = new HashMap<String, Object>();
        map.put("userId", userId);
        map.put("actionCode", actionCode);
        if (actionTime == null) {
            creditHisDoList= creditHisMapper.getCreditByUserIdAndActionCode(map);
        }else{
            map.put("actionTime", actionTime);
            creditHisDoList= creditHisMapper.getCreditByUserIdAndActionCodeAndDate(map);
        }
        if(creditHisDoList!=null && creditHisDoList.size()>0){
            for (CreditHisDO creditHisDO : creditHisDoList)
            {
                creditHisList.add(new CreditHis(creditHisDO));
            }
        }
        return creditHisList;
    }

    @Override
    public int getCreditHisByActionCodeList(long userId, List actionCodeList, Date actionTime) {
        HashMap<String, Object> map = new HashMap<String, Object>();
        map.put("userId", userId);
        map.put("actionCodeList", actionCodeList);
        if (actionTime == null) {
            return creditHisMapper.getCreditByUserIdAndActionCodeList(map);
        }
        map.put("actionTime", actionTime);
        return creditHisMapper.getCreditByUserIdAndActionCodeListAndDate(map);

    }

    @Override
    public int getCreditHisByActionCodePrefix(long userId, String actionCodePrefix, Date actionTime) {
        HashMap<String, Object> map = new HashMap<String, Object>();
        map.put("userId", userId);
        map.put("actionCodePrefix", actionCodePrefix);
        if (actionTime == null) {
            return creditHisMapper.getCreditByUserIdAndActionCodePrefix(map);
        }
        map.put("actionTime", actionTime);
        return creditHisMapper.getCreditByUserIdAndActionCodePrefixAndDate(map);
    }

}
